package com.sky.business.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.sky.business.sys.domain.CameraPreset;
import com.sky.business.sys.dto.CameraRtspInfo;
import com.sky.business.sys.dto.OnvifDeviceInfo;
import com.sky.business.sys.mapper.CameraConfigMapper;
import com.sky.business.sys.mapper.PresetMapper;
import com.sky.business.sys.service.OnvifService;
import com.sky.common.task.OnvifControlTask;
import de.onvif.soap.OnvifDevice;
import de.onvif.soap.devices.ImagingDevices;
import de.onvif.soap.devices.InitialDevices;
import de.onvif.soap.devices.MediaDevices;
import de.onvif.soap.devices.PtzDevices;
import lombok.extern.slf4j.Slf4j;
import org.onvif.ver10.device.wsdl.GetDeviceInformationResponse;
import org.onvif.ver10.schema.ImagingSettings20;
import org.onvif.ver10.schema.IrCutFilterMode;
import org.onvif.ver10.schema.Profile;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.xml.soap.SOAPException;
import java.net.ConnectException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ScheduledFuture;

@Service
@Slf4j
public class OnvifServiceImpl implements OnvifService {

    @Resource
    private PresetMapper presetMapper;

    @Resource
    private CameraConfigMapper cameraConfigMapper;

    private static final Map<String, ScheduledFuture<?>> TASK_MAP = new HashMap<>();

    private static final OnvifControlTask onvifControlTask = new OnvifControlTask();

    public static String getProfile(OnvifDevice nvt) {
        List<Profile> profiles = nvt.getDevices().getProfiles();
        return profiles.get(0).getToken();
    }

    public static String getVidoSourceToken(OnvifDevice nvt) {
        List<Profile> profiles = nvt.getDevices().getProfiles();
        String token = null;
        for (Profile profile : profiles) {
            token = profile.getVideoSourceConfiguration().getToken();
        }
        return token;
    }

    @Override
    public void move(String ip, int port, String username, String password, float x, float y, float zoom) throws SOAPException, ConnectException {
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        Date nvtDate = nvt.getDevices().getDate();
        String profileToken = getProfile(nvt);
        PtzDevices ptzDevices = nvt.getPtz();
        Map<String, Object> param = new HashMap<>();
        param.put("profileToken", profileToken);
        param.put("ptzDevices", ptzDevices);
        param.put("x", x);
        param.put("y", y);
        param.put("zoom", zoom);

//        ScheduledFuture<?> scheduledFuture = onvifControlTask.startMoveTask(OnvifServiceImpl::onvifMove, 1000, param);
//        TASK_MAP.put(ip, scheduledFuture);
        ptzDevices.continuousMove(String.valueOf(profileToken), x, y, zoom);
    }

    @Override
    public void stop(String ip, int port, String username, String password) throws SOAPException, ConnectException {
//        ScheduledFuture<?> scheduledFuture = TASK_MAP.get(ip);
//        onvifControlTask.stopTask(scheduledFuture);
//        TASK_MAP.remove(ip);
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        String profileToken = getProfile(nvt);
        PtzDevices ptzDevices = nvt.getPtz();
        ptzDevices.stopMove(profileToken);
    }

    @Override
    public void setPreset(Integer id, String ip, int port, String username, String password) throws SOAPException, ConnectException {
        Integer maxId = presetMapper.getMaxId();
        maxId = maxId == null ? 0 : maxId;
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        String profileToken = getProfile(nvt);
        PtzDevices ptzDevices = nvt.getPtz();
        String presetToken = ptzDevices.setPreset("preset" + (maxId + 1), profileToken);
        CameraPreset preset = new CameraPreset();
        preset.setPresetName("preset" + (maxId + 1));
        preset.setPresetToken(presetToken);
        preset.setCameraConfigId(id);
        presetMapper.insert(preset);
    }

    @Override
    public List<CameraPreset> getPresetData(Integer cameraConfigId) {
        LambdaQueryWrapper<CameraPreset> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CameraPreset::getCameraConfigId, cameraConfigId).orderByAsc(CameraPreset::getId);
        return presetMapper.selectList(queryWrapper);
    }

    @Override
    public void gotoPreset(String ip, int port, String username, String password, String presetToken) throws SOAPException, ConnectException {
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        String profileToken = getProfile(nvt);
        PtzDevices ptzDevices = nvt.getPtz();
        ptzDevices.gotoPreset(presetToken, profileToken);
    }

    @Override
    public void moveFocus(String ip, int port, String username, String password, boolean negative) throws SOAPException, ConnectException {
        ScheduledFuture<?> scheduledFuture = TASK_MAP.get(ip + "_focus");
        //如果已有在执行任务则中断
        if (scheduledFuture != null) {
            scheduledFuture.cancel(true);
        }
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        String videoSourceToken = getVidoSourceToken(nvt);
        ImagingDevices imageDevices = nvt.getImaging();
        LocalDateTime endTime = LocalDateTime.now().plusMinutes(1);
        Map<String, Object> param = new HashMap<>();

        param.put("imageDevices", imageDevices);
        param.put("videoSourceToken", videoSourceToken);
        param.put("negative", negative);
        param.put("endTime", endTime);
        scheduledFuture = onvifControlTask.startMoveFocusTask(this::moveFocusTask, 1000, param);
        TASK_MAP.put(ip + "_focus", scheduledFuture);
    }

    @Override
    public boolean stopMoveFocus(String ip, int port, String username, String password) throws SOAPException, ConnectException {
        ScheduledFuture<?> scheduledFuture = TASK_MAP.get(ip + "_focus");
        if (scheduledFuture != null) {
            scheduledFuture.cancel(true);
            TASK_MAP.remove(ip + "_focus");
        }
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        String videoSourceToken = getVidoSourceToken(nvt);
        ImagingDevices imageDevices = nvt.getImaging();
        return imageDevices.stopFocus(videoSourceToken);
    }

    @Override
    public List<CameraRtspInfo> getRtspStreamList(String ip, int port, String username, String password) throws SOAPException, ConnectException {
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        MediaDevices mediaDevices = nvt.getMedia();
        List<Profile> profiles = nvt.getDevices().getProfiles();
        List<CameraRtspInfo> result = new ArrayList<>();
        for (Profile profile : profiles) {
            int width = profile.getVideoEncoderConfiguration().getResolution().getWidth();
            int height = profile.getVideoEncoderConfiguration().getResolution().getHeight();
            int bitrateLimit = profile.getVideoEncoderConfiguration().getRateControl().getBitrateLimit();
            String profileToken = profile.getToken();
            String rtspUrl = mediaDevices.getRTSPStreamUri(profileToken);
            CameraRtspInfo profileInfo = new CameraRtspInfo(width, height, bitrateLimit, rtspUrl);
            result.add(profileInfo);
        }
        return result;
    }

    @Override
    public OnvifDeviceInfo getDeviceInformation(String ip, int port, String username, String password) throws SOAPException, ConnectException {
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        InitialDevices initialDevices = nvt.getDevices();
        GetDeviceInformationResponse getDeviceInformationResponse = initialDevices.getDeviceInformation();
        String manufacturer = getDeviceInformationResponse.getManufacturer();
        String model = getDeviceInformationResponse.getModel();
        String firmwareVersion = getDeviceInformationResponse.getFirmwareVersion();
        String serialNumber = getDeviceInformationResponse.getSerialNumber();
        String hardwareId = getDeviceInformationResponse.getHardwareId();
        return new OnvifDeviceInfo(manufacturer,model,firmwareVersion,serialNumber,hardwareId);
    }

    @Override
    public String getFhdRtspUrl(List<CameraRtspInfo> cameraRtspInfoList) {
        Collections.sort(cameraRtspInfoList);
        CameraRtspInfo info = null;
        for (int i = 0; i < cameraRtspInfoList.size(); i++) {
            CameraRtspInfo currInfo = cameraRtspInfoList.get(i);
            if (currInfo.getWidth() > 1920 || currInfo.getHeight() > 1080) {
                break;
            }
            info = currInfo;
        }
        if(info==null){
            info = cameraRtspInfoList.get(0);
        }
        return info.getRtspUrl();
    }

    @Override
    public void delPreset(Integer id, String ip, int port, String username, String password, String presetToken) throws SOAPException, ConnectException {
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        String profileToken = getProfile(nvt);
        PtzDevices ptzDevices = nvt.getPtz();
        boolean removeFlag = ptzDevices.removePreset(presetToken, profileToken);
        if (removeFlag) {
            presetMapper.deleteById(id);
        }
    }

    @Override
    public void updatePresetName(Integer presetId, String ip, int port, String username, String password, String name, String presetToken) throws SOAPException, ConnectException {
        OnvifDevice nvt = new OnvifDevice(ip, port, username, password);
        String profileToken = getProfile(nvt);
        PtzDevices ptzDevices = nvt.getPtz();
        ptzDevices.setPreset(name, presetToken, profileToken);
        LambdaUpdateWrapper<CameraPreset> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(CameraPreset::getPresetName, name).eq(CameraPreset::getId, presetId);
        presetMapper.update(wrapper);
    }

    /**
     * @param param
     */
    private static void onvifMove(Map<String, Object> param) {
        log.info("定时任务执行时间: " + System.currentTimeMillis());
        for (String key : param.keySet()) {
            log.info(key + ":" + param.get(key));
        }
        PtzDevices ptzDevices = (PtzDevices) param.get("ptzDevices");
        ptzDevices.continuousMove(String.valueOf(param.get("profileToken")), (float) param.get("x"), (float) param.get("y"), (float) param.get("zoom"));
    }


    /**
     * 聚焦+-
     *
     * @param param 相关参数
     */
    private void moveFocusTask(Map<String, Object> param) {
        ImagingDevices imageDevices = (ImagingDevices) param.get("imageDevices");
        String videoSourceToken = (String) (param.get("videoSourceToken"));
        Boolean negative = (Boolean) param.get("negative");
        LocalDateTime endTime = (LocalDateTime) param.get("endTime");
        LocalDateTime now = LocalDateTime.now();
        if (now.isAfter(endTime)) {
            return;
        }
        float speed = 0.5f;
        if (negative) {
            speed = -0.5f;
        }
        imageDevices.continuousFocus(videoSourceToken, speed);
    }

    @Override
    public void setIrCutFilterMode(String ip, int port, String username, String password, int mode) throws SOAPException, ConnectException {
        OnvifDevice nvt = new OnvifDevice(ip,port, username, password);
        String videoSourceToken = getVidoSourceToken(nvt);
        ImagingDevices imagingDevices = nvt.getImaging();
        ImagingSettings20 imagingSettings20 = new ImagingSettings20();
        IrCutFilterMode irCutFilterMode;
        if(mode==1){
            irCutFilterMode  = IrCutFilterMode.ON;
        }else if(mode==2){
            irCutFilterMode = IrCutFilterMode.OFF;
        }else if(mode ==3){
            irCutFilterMode = IrCutFilterMode.AUTO;
        }else{
            throw new IllegalArgumentException("irCutFilterMode="+mode);
        }
        imagingSettings20.setIrCutFilter(irCutFilterMode);
        imagingDevices.setImagingSettings(videoSourceToken,imagingSettings20);
    }
}
